package com.ssrs.platform.controller.handler;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.ssrs.framework.Config;
import com.ssrs.framework.security.annotation.Priv;
import com.ssrs.platform.config.ResourceRoot;
import org.springframework.http.CacheControl;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author ssrs
 */
@RestController
public class PreviewURLHandler {

    static HashMap<String, String> mimeMap = new HashMap<String, String>();

    static {
        mimeMap.put("abs", "audio/x-mpeg");
        mimeMap.put("ai", "application/postscript");
        mimeMap.put("aif", "audio/x-aiff");
        mimeMap.put("aifc", "audio/x-aiff");
        mimeMap.put("aiff", "audio/x-aiff");
        mimeMap.put("aim", "application/x-aim");
        mimeMap.put("art", "image/x-jg");
        mimeMap.put("asf", "video/x-ms-asf");
        mimeMap.put("asx", "video/x-ms-asf");
        mimeMap.put("au", "audio/basic");
        mimeMap.put("avi", "video/x-msvideo");
        mimeMap.put("avx", "video/x-rad-screenplay");
        mimeMap.put("bcpio", "application/x-bcpio");
        mimeMap.put("bin", "application/octet-stream");
        mimeMap.put("ttf", "application/octet-stream");
        mimeMap.put("otf", "application/octet-stream");
        mimeMap.put("eot", "application/vnd.ms-fontobject");
        mimeMap.put("bmp", "image/bmp");
        mimeMap.put("body", "text/html");
        mimeMap.put("cdf", "application/x-cdf");
        mimeMap.put("cer", "application/x-x509-ca-cert");
        mimeMap.put("class", "application/java");
        mimeMap.put("cpio", "application/x-cpio");
        mimeMap.put("csh", "application/x-csh");
        mimeMap.put("css", "text/css");
        mimeMap.put("dib", "image/bmp");
        mimeMap.put("doc", "application/msword");
        mimeMap.put("dtd", "application/xml-dtd");
        mimeMap.put("dv", "video/x-dv");
        mimeMap.put("dvi", "application/x-dvi");
        mimeMap.put("eps", "application/postscript");
        mimeMap.put("etx", "text/x-setext");
        mimeMap.put("exe", "application/octet-stream");
        mimeMap.put("gif", "image/gif");
        mimeMap.put("gtar", "application/x-gtar");
        mimeMap.put("gz", "application/x-gzip");
        mimeMap.put("hdf", "application/x-hdf");
        mimeMap.put("hqx", "application/mac-binhex40");
        mimeMap.put("htc", "text/x-component");
        mimeMap.put("htm", "text/html");
        mimeMap.put("html", "text/html");
        mimeMap.put("hqx", "application/mac-binhex40");
        mimeMap.put("ief", "image/ief");
        mimeMap.put("jad", "text/vnd.sun.j2me.app-descriptor");
        mimeMap.put("jar", "application/java-archive");
        mimeMap.put("java", "text/plain");
        mimeMap.put("jnlp", "application/x-java-jnlp-file");
        mimeMap.put("jpe", "image/jpeg");
        mimeMap.put("jpeg", "image/jpeg");
        mimeMap.put("jpg", "image/jpeg");
        mimeMap.put("js", "text/javascript");
        mimeMap.put("jsf", "text/plain");
        mimeMap.put("jspf", "text/plain");
        mimeMap.put("kar", "audio/x-midi");
        mimeMap.put("latex", "application/x-latex");
        mimeMap.put("m3u", "audio/x-mpegurl");
        mimeMap.put("mac", "image/x-macpaint");
        mimeMap.put("man", "application/x-troff-man");
        mimeMap.put("mathml", "application/mathml+xml");
        mimeMap.put("me", "application/x-troff-me");
        mimeMap.put("mid", "audio/x-midi");
        mimeMap.put("midi", "audio/x-midi");
        mimeMap.put("mif", "application/x-mif");
        mimeMap.put("mov", "video/quicktime");
        mimeMap.put("movie", "video/x-sgi-movie");
        mimeMap.put("mp1", "audio/x-mpeg");
        mimeMap.put("mp2", "audio/x-mpeg");
        mimeMap.put("mp3", "audio/x-mpeg");
        mimeMap.put("mp4", "video/mp4");
        mimeMap.put("mpa", "audio/x-mpeg");
        mimeMap.put("mpe", "video/mpeg");
        mimeMap.put("mpeg", "video/mpeg");
        mimeMap.put("mpega", "audio/x-mpeg");
        mimeMap.put("mpg", "video/mpeg");
        mimeMap.put("mpv2", "video/mpeg2");
        mimeMap.put("ms", "application/x-wais-source");
        mimeMap.put("nc", "application/x-netcdf");
        mimeMap.put("oda", "application/oda");
        mimeMap.put("odb", "application/vnd.oasis.opendocument.database");
        mimeMap.put("odc", "application/vnd.oasis.opendocument.chart");
        mimeMap.put("odf", "application/vnd.oasis.opendocument.formula");
        mimeMap.put("odg", "application/vnd.oasis.opendocument.graphics");
        mimeMap.put("odi", "application/vnd.oasis.opendocument.image");
        mimeMap.put("odm", "application/vnd.oasis.opendocument.text-master");
        mimeMap.put("odp", "application/vnd.oasis.opendocument.presentation");
        mimeMap.put("ods", "application/vnd.oasis.opendocument.spreadsheet");
        mimeMap.put("odt", "application/vnd.oasis.opendocument.text");
        mimeMap.put("ogg", "application/ogg");
        mimeMap.put("otg", "application/vnd.oasis.opendocument.graphics-template");
        mimeMap.put("oth", "application/vnd.oasis.opendocument.text-web");
        mimeMap.put("otp", "application/vnd.oasis.opendocument.presentation-template");
        mimeMap.put("ots", "application/vnd.oasis.opendocument.spreadsheet-template");
        mimeMap.put("ott", "application/vnd.oasis.opendocument.text-template");
        mimeMap.put("pbm", "image/x-portable-bitmimeMap");
        mimeMap.put("pct", "image/pict");
        mimeMap.put("pdf", "application/pdf");
        mimeMap.put("pgm", "image/x-portable-graymimeMap");
        mimeMap.put("pic", "image/pict");
        mimeMap.put("pict", "image/pict");
        mimeMap.put("pls", "audio/x-scpls");
        mimeMap.put("png", "image/png");
        mimeMap.put("pnm", "image/x-portable-anymimeMap");
        mimeMap.put("pnt", "image/x-macpaint");
        mimeMap.put("ppm", "image/x-portable-pixmimeMap");
        mimeMap.put("ppt", "application/vnd.ms-powerpoint");
        mimeMap.put("pps", "application/vnd.ms-powerpoint");
        mimeMap.put("ps", "application/postscript");
        mimeMap.put("psd", "image/x-photoshop");
        mimeMap.put("qt", "video/quicktime");
        mimeMap.put("qti", "image/x-quicktime");
        mimeMap.put("qtif", "image/x-quicktime");
        mimeMap.put("ras", "image/x-cmu-raster");
        mimeMap.put("rdf", "application/rdf+xml");
        mimeMap.put("rgb", "image/x-rgb");
        mimeMap.put("rm", "application/vnd.rn-realmedia");
        mimeMap.put("roff", "application/x-troff");
        mimeMap.put("rtf", "application/rtf");
        mimeMap.put("rtx", "text/richtext");
        mimeMap.put("sh", "application/x-sh");
        mimeMap.put("shar", "application/x-shar");
        mimeMap.put("smf", "audio/x-midi");
        mimeMap.put("sit", "application/x-stuffit");
        mimeMap.put("snd", "audio/basic");
        mimeMap.put("src", "application/x-wais-source");
        mimeMap.put("sv4cpio", "application/x-sv4cpio");
        mimeMap.put("sv4crc", "application/x-sv4crc");
        mimeMap.put("svg", "image/svg+xml");
        mimeMap.put("svgz", "image/svg+xml");
        mimeMap.put("swf", "application/x-shockwave-flash");
        mimeMap.put("t", "application/x-troff");
        mimeMap.put("tar", "application/x-tar");
        mimeMap.put("tcl", "application/x-tcl");
        mimeMap.put("tex", "application/x-tex");
        mimeMap.put("texi", "application/x-texinfo");
        mimeMap.put("texinfo", "application/x-texinfo");
        mimeMap.put("tif", "image/tiff");
        mimeMap.put("tiff", "image/tiff");
        mimeMap.put("tr", "application/x-troff");
        mimeMap.put("tsv", "text/tab-separated-values");
        mimeMap.put("txt", "text/plain");
        mimeMap.put("ulw", "audio/basic");
        mimeMap.put("ustar", "application/x-ustar");
        mimeMap.put("vxml", "application/voicexml+xml");
        mimeMap.put("xbm", "image/x-xbitmimeMap");
        mimeMap.put("xht", "application/xhtml+xml");
        mimeMap.put("xhtml", "application/xhtml+xml");
        mimeMap.put("xls", "application/vnd.ms-excel");
        mimeMap.put("xml", "application/xml");
        mimeMap.put("xpm", "image/x-xpixmimeMap");
        mimeMap.put("xsl", "application/xml");
        mimeMap.put("xslt", "application/xslt+xml");
        mimeMap.put("xul", "application/vnd.mozilla.xul+xml");
        mimeMap.put("xwd", "image/x-xwindowdump");
        mimeMap.put("vsd", "application/x-visio");
        mimeMap.put("wav", "audio/x-wav");
        mimeMap.put("wbmp", "image/vnd.wap.wbmp");
        mimeMap.put("wml", "text/vnd.wap.wml");
        mimeMap.put("wmlc", "application/vnd.wap.wmlc");
        mimeMap.put("wmls", "text/vnd.wap.wmlscript");
        mimeMap.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
        mimeMap.put("wmv", "video/x-ms-wmv");
        mimeMap.put("wrl", "x-world/x-vrml");
        mimeMap.put("wspolicy", "application/wspolicy+xml");
        mimeMap.put("Z", "application/x-compress");
        mimeMap.put("z", "application/x-compress");
        mimeMap.put("zip", "application/zip");

    }

    /**
     * 只用在后台管理文件预览，前台模式不要使用。前台模式使用nginx或其他文件服务器预览
     *
     * @param request
     * @param response
     * @throws IOException
     * @throws ServletException
     */
    @Priv(login = false)
    @GetMapping("/preview/**")
    public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        if (Config.getServletMajorVersion() == 2 && Config.getServletMinorVersion() == 3) {
            response.setContentType("text/html;charset=" + Config.getGlobalCharset());
        } else {
            response.setCharacterEncoding(Config.getGlobalCharset());
        }
        String headerValue = CacheControl.maxAge(0, TimeUnit.SECONDS).getHeaderValue();
        response.addHeader("Cache-Control", headerValue);
        String contextPath = request.getContextPath();
        String path = "";
        String fileName = "";
        String url = request.getRequestURI();
        url = URLUtil.decode(url);
        int index = url.lastIndexOf("/");
        if (index > 1) {
            path = url.substring(0, index);
            fileName = url.substring(index + 1);
            String prefix = contextPath + "/preview/";
            if (path.startsWith(prefix)) {
                path = path.substring(prefix.length());
            }
        } else {
            fileName = "index.html";
        }
        fileName = ResourceRoot.getValue() + path + "/" + fileName;
        fileName = FileUtil.normalize(fileName);
        File f = new File(fileName);
        if (f.exists() && f.isFile()) {
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Last-Modified", DateUtil.format(new Date(f.lastModified()), DatePattern.HTTP_DATETIME_FORMAT));
            String since = request.getHeader("If-Modified-Since");
            if (ObjectUtil.isNotEmpty(since)) {
                Date d = DateUtil.parse(since, DatePattern.HTTP_DATETIME_FORMAT);
                if (d != null && d.getTime() >= f.lastModified() / 1000 * 1000) {
                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                    return;// 直接从缓存中获取
                }
            }
            String ext = FileUtil.getSuffix(url);
            if (StrUtil.isNotEmpty(ext)) {
                String mimeType = mimeMap.get(ext);
                if (mimeType != null) {
                    response.setHeader("Content-Type", mimeType);
                } else if (StrUtil.contains(ext, "html")) {
                    response.setHeader("Content-Type", "text/html");
                } else {
                    response.setHeader("Content-Type", "application/octet-stream");
                }
            }

            // 解析Range
            if (StrUtil.isNotEmpty(request.getHeader("Range"))) {
                String rangeStr = request.getHeader("Range").trim();
                if (rangeStr.startsWith("bytes=")) {
                    rangeStr = rangeStr.substring(6);
                    String[] range = null;
                    if (rangeStr.indexOf(",") > -1) {
                        range = rangeStr.split(",");
                    } else {
                        range = new String[]{rangeStr};
                    }
                    for (String r : range) {
                        write(response, f, r);
                    }
                }
            }
            write(response, f, -1, -1);
        } else {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        }
        response.flushBuffer();
        return;
    }

    private static void write(HttpServletResponse response, File f, String range) throws IOException {
        long start, end;
        String[] arr = StrUtil.split(range, "-");
        if (StrUtil.isEmpty(arr[0])) {
            // bytes=-500 : 表示最后500个字节
            start = f.length() - Integer.valueOf(arr[1]);
            end = f.length() - 1;
        } else {
            start = Long.valueOf(arr[0]);
            if (StrUtil.isEmpty(arr[1])) {
                end = f.length() - 1; // bytes=500- : 表示500字节以后的范围
            } else {
                end = Long.valueOf(arr[1]);
            }
        }
        write(response, f, (int) start, (int) end);
    }

    private static void write(HttpServletResponse response, File f, int start, int end) throws IOException {
        FileInputStream fis = new FileInputStream(f);
        OutputStream os = response.getOutputStream();
        int len = 0;
        long size = f.length();
        // response.setHeader("Content-Length", "" + size);
        byte[] bs = new byte[1024 * 100];
        if (start != -1) {
            response.setHeader("Accept-Ranges", "bytes");
            response.setStatus(206);
            if (start >= size || start > end) {
                response.setHeader("Content-Range", "bytes 0-0/" + size);
                // 超出范围
            } else {
                response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + size);
                fis.skip(start);
                int pos = start;
                while ((len = fis.read(bs)) != -1) {
                    if (pos + len >= end) {
                        os.write(bs, 0, end - pos);
                        pos = end;
                        break;
                    } else {
                        os.write(bs, 0, len);
                        pos += len;
                    }
                    response.flushBuffer();
                }
            }
        } else {
            while ((len = fis.read(bs)) != -1) {
                os.write(bs, 0, len);
                response.flushBuffer();
            }
        }
        fis.close();
    }

}
